#include "coreMesh.h"
#include <cassert>
#include "includes.h"
#include "text.h"
#include <iostream> //to output
#include "utils.h"
#include "scene.h"


void coreMesh::clear()
{
	vertices.clear();
	normals.clear();
	uvs.clear();
	indices.clear();
}

void coreMesh::renderDebug()
{
glDisable( GL_TEXTURE_2D );
glColor3f(1,1,1);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
assert(uvs.size() ==  vertices.size() && "VECTORES NO COINCIDEN" );

	glBegin( GL_TRIANGLES );
		for (int i = 0; i < vertices.size(); i++)
		{
			glTexCoord2f(uvs[i].x, uvs[i].y);
			glVertex3fv( (float*)&vertices[i] );
		}
	glEnd();

}

void coreMesh::render()
{
	int offset, antOffset = 0;
	assert(vertices.size() && "No vertices in this mesh");
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0 ,&vertices[0] );

	if (normals.size())
	{
		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer(GL_FLOAT, 0, &normals[0] );
	}
	
	if (uvs.size())
	{
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2,GL_FLOAT,0 , &uvs[0] );
	}

	//pintamos sin mutli
	if (texturesID.size() == 1)
	{
		if(!Scene::getScene()->ActivateTexture(texturesID[0]))
				std::cout<<"Error Loading texture \""<<texturesID[0]<<"\""<<std::endl;
		glDrawArrays(GL_TRIANGLES, 0 , vertices.size() );
		glDisableClientState(GL_VERTEX_ARRAY);
	}
	// Pintamos con multitexturas
	else if (texturesID.size() > 1){
		for(int i = 0; i < texturesID.size(); i++){
			
			if (i == texturesID.size() - 1) 
				offset = vertices.size() - antOffset;
			else 
				offset = texturesOffset[i]*3 - antOffset;

			//activamos textura
			if(!Scene::getScene()->ActivateTexture(texturesID[i]))
				std::cout<<"Error Loading texture \""<<texturesID[i]<<"\""<<std::endl;	
			
			glDrawArrays(GL_TRIANGLES, antOffset, offset);
            antOffset += offset;
		}
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	
	if (normals.size())
		glDisableClientState(GL_NORMAL_ARRAY);
	if (uvs.size())
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
}

void coreMesh::addQuad(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Vector3 color)
{
	vertices.push_back(v1);
	//colors.push_back( color );
	vertices.push_back(v2);
	//colors.push_back( color );
	vertices.push_back(v3);
	//colors.push_back( color );

	vertices.push_back(v2);
	//colors.push_back( color );
	vertices.push_back(v3);
	//colors.push_back( color );
	vertices.push_back(v4);
	//colors.push_back( color );
}

void coreMesh::createCube(float size)
{
	float hs = size * 0.5;
	Vector3 vs[8];

	Vector3 red(1,0,0);
	Vector3 green(0,1,0);
	Vector3 blue(0,0,1);

	//vertices of a cube
	vs[0] = Vector3(hs,hs,hs);
	vs[1] = Vector3(hs,hs,-hs);
	vs[2] = Vector3(hs,-hs,hs);
	vs[3] = Vector3(hs,-hs,-hs);
	vs[4] = Vector3(-hs,hs,hs);
	vs[5] = Vector3(-hs,hs,-hs);
	vs[6] = Vector3(-hs,-hs,hs);
	vs[7] = Vector3(-hs,-hs,-hs);

	//coplanares: 0123-4567 0145-2367 0246-1357

	//create the triangles
	addQuad( vs[0], vs[1], vs[2], vs[3], red );
	addQuad( vs[4], vs[5], vs[6], vs[7], red );

	addQuad( vs[0], vs[1], vs[4], vs[5], green );
	addQuad( vs[2], vs[3], vs[6], vs[7], green );

	addQuad( vs[0], vs[2], vs[4], vs[6], blue );
	addQuad( vs[1], vs[3], vs[5], vs[7], blue );
}

bool coreMesh::parseAse(std::string file){

	std::vector< Vector3u > faces;
	std::vector< Vector2 > tVertices;
	std::vector< Vector3 > facesPlain;

	long start = getTime();
	int numVertices;
	int numFaces;
	int numTVertices;
	int numTFaces;
	int subMaterials;
	size_t pos;
	coreID = file;
	coreID = coreID.substr(coreID.find("/")+1,coreID.length());
	coreID = coreID.substr(0,coreID.length()-4);
	std::string filename = "ase/"+coreID+".adf";

	//comprovamos si existe el fichero .asf
	//sino leemos el .ase y creamos el .asf
	if (false){//FILE * f = fopen(filename.data(),"r")){
		//fclose(f);
		ReadDumpedFile();
	} else {
		text * ase = new text(file.data());
		if (ase == NULL){
			return false;
		}
		std::cout<<"Comienza lectura fichero: "<<file<<std::endl;
		//miramos la cantidad de materiales
		ase->seek("*MATERIAL_COUNT");
		if(ase->getint() > 1) std::cout<<"Oh! No!! mas de un material!!"<<std::endl;
		subMaterials = 0;

		//vemos si tiene mas de una textura
		multiTexture = false;
		if ( ase->countword ("*NUMSUBMTLS") > 0){
			 ase->seek("*NUMSUBMTLS");
			subMaterials = ase->getint();
		} 
		std::vector<std::string> texVec;
		if (subMaterials > 0){
			//leemos todas las texturas
			texturesID.resize(subMaterials);
			multiTexture = true;
			for(int i = 0; i< subMaterials;++i){
				ase->seek("*BITMAP");
				std::string name = ase->getword();
				for(int j = 0;j < name.length();j++){
					name[j] = tolower(name[j]);
				}
				

				//sacamos comillas			
				name = name.substr(1,name.length()-2);
				//nos quedamos solo con el nombre de archivo
				//eliminamos la ruta
				
				pos = name.find_last_of("/");
				if ( pos != std::string::npos){
					name = name.substr(pos, name.length());
				} else {
					pos = name.find_last_of("\\");
					if ( pos != std::string::npos){
						name = name.substr(pos+1, name.length());
					}
				}
				//quitamos ".tga"
				pos = name.find(".tga");
				if ( pos != std::string::npos){
					name = name.substr(0, pos);
				}
				
				texturesID[i] = name;
				texVec.push_back(name);
			}
			Scene::getScene()->LoadTextures(texVec);
			std::cout << "CoreMesh :: Textures Loaded" <<std::endl;
		}
		else{
			multiTexture = false;
			if (ase->countword("*BITMAP") > 0){
				ase->seek("*BITMAP");
				std::string name = ase->getword();
				for(int i = 0; i<name.length();i++){
					name[i] = tolower(name[i]);
				}
				
				//sacamos las comillas que lleva el nombre
				name = name.substr(1,name.length()-2);
				
				//nos quedamos solo con el nombre de archivo
				//eliminamos la ruta
				size_t pos;
				pos = name.find_last_of("/");
				if ( pos != std::string::npos){
					name = name.substr(pos, name.length());
				} else {
					pos = name.find_last_of("\\");
					if ( pos != std::string::npos){
						name = name.substr(pos+1, name.length());
					}
				}
				//eliminamos ".tga"
				pos = name.find(".tga");
				if ( pos != std::string::npos){
					name = name.substr(0,pos);
				}
				texturesID.push_back(name);
				texVec.push_back(name);
				Scene::getScene()->LoadTextures(texVec);
			}
		}
		ase->seek("*MESH_NUMVERTEX");
		numVertices = ase->getint();
		ase->getword();
		numFaces = ase->getint();
		ase->seek("*MESH_VERTEX");
		float x,y,z,v1,v2,v3,tv1,tv2,tv3,u,v;
		float delta = 1;
		//Obtenemos vertices
		facesPlain.resize(numVertices);
		//std::cout<<"Comienzo lectura VERTICES"<<std::endl;
		for(int i = 0; i < numVertices; i++){
			ase->getint();
			
			x = -ase->getfloat();
			z = ase->getfloat();
			y = ase->getfloat();
			x *= delta;
			y *= delta;
			z *= delta;
			ase->getword();
			media.x += x;
			media.y += y;
			media.z += z;
			facesPlain[i] = (Vector3(x,y,z));
		}
		media.x /= numVertices;
		media.y /= numVertices;
		media.z /= numVertices;
		//buscamos punto mas lejano de la media;
		float dist,aux;
		dist = 0;
		for (int i = 0; i< facesPlain.size();i++){
			aux = Distancia(media,facesPlain[i]); 
			if ( aux > dist) {
				lejano = facesPlain[i];
				dist = aux;
			}
		}
		std::cout<<"Fin lectura VERTICES"<<std::endl;
		//Obtenemos caras
		int t_old;
		faces.resize(numFaces);
		vertices.resize(numFaces*3);
		t_old = 0;
		for(int i = 0; i < numFaces; i++){
			ase->seek("*MESH_FACE");
			ase->getword();
			ase->getword();
			v1 = ase->getint();
			ase->getword();
			v2 = ase->getint();
			ase->getword();
			v3 = ase->getint();
			if (subMaterials > 0){
				ase->seek("*MESH_MTLID");
				int t = ase->getint();
				if (t != t_old){ 
					t_old = t;
					texturesOffset.push_back(i);
				}
			}
			vertices[i*3] = (facesPlain[v1]);
			vertices[i*3+1] = (facesPlain[v2]);
			vertices[i*3+2] = (facesPlain[v3]);
		}
		std::cout<<"Fin lectura CARAS"<<std::endl;
		//Obtenemos Tvertices

		ase->seek("*MESH_NUMTVERTEX");
		numTVertices = ase->getint();
		tVertices.reserve(numTVertices);
		tVertices.resize(numTVertices);
		for(int i = 0; i < numTVertices; i++){
			ase->seek("*MESH_TVERT");
			ase->getint();
			u = ase->getfloat();
			v = ase->getfloat();
			ase->getfloat();
			tVertices[i] = (Vector2(u,v));
		}
		std::cout<<"Fin lectura TVERTICES"<<std::endl;
		//Obtenemos T Faces
		ase->seek("*MESH_NUMTVFACES");
		numTFaces = ase->getint();
		uvs.resize(numTFaces*3);
		for(int i = 0; i < numTFaces; i++){
			ase->seek("*MESH_TFACE");
			ase->getint();
			tv1 = ase->getint();
			tv2 = ase->getint();
			tv3 = ase->getint();
			uvs[i*3] = tVertices[ tv1 ];
			uvs[i*3+1] = tVertices[ tv2 ];
			uvs[i*3+2] = tVertices[ tv3 ];
		}
		std::cout<<"Fin lectura TCARAS"<<std::endl;

		ase->seek("*MESH_NORMALS");
		normals.resize(uvs.size());
		for(int i = 0; i < uvs.size();i++){

			ase->seek("*MESH_VERTEXNORMAL");
			ase->getint();
			x = -ase->getfloat();
			z = ase->getfloat();
			y = ase->getfloat();
			normals[i] = (Vector3(x,y,z));
		}


		std::cout<<"Fin Parseado "<< file << std::endl<<"Tiempo transcurrido : " <<getTime()-start << "ms." <<std::endl;

		//DumpToFile();	

	}

	start = getTime();
	//std::cout<<"Generando Modelo de Colisiones"<<std::endl;
	colmodel = newCollisionModel3D();
	//std::cout<<"Generando Modelo de Colisiones -- a"<<std::endl;
	colmodel->setTriangleNumber(numFaces);
	//std::cout<<"Generando Modelo de Colisiones -- b "<<std::endl;
	for(int i = 0; i < numFaces; i++)
	{
		colmodel->addTriangle(vertices[i*3].x, vertices[i*3].y, vertices[i*3].z, 
                              vertices[i*3+1].x, vertices[i*3+1].y, vertices[i*3+1].z, 
                              vertices[i*3+1].x, vertices[i*3+1].y, vertices[i*3+1].z );
	}
	//std::cout<<"Generando Modelo de Colisiones -- c"<<std::endl;
	try	{
		colmodel->finalize();
	}
	catch(Inconsistency()){
	
		int i = 0;
		i++;
	}
	std::cout<<"Fin Modelo Colisiones. ";//<< file << std::endl<<"Tiempo transcurrido : " <<getTime()-start << "ms." <<std::endl;
	return true;
}


float coreMesh::Distancia(Vector3 a, Vector3 b){

	return fabs((a.x-b.x)+(a.y-b.y)+(a.z-b.z));
}

void coreMesh::DumpToFile(){

	/*
		Estructura de fichero binario ase
		ASE Dump File
		xxx.adf
		numero_vertices data_vertices
		numero_uvs data_uvs
		numero_normales data_normales
		numero_texturas
		numero_caracateres nombre_textura_1
		numero_caracateres nombre_textura_2
		numero_caracateres nombre_textura_n

	*/
	
	FILE * ptr_fp;
	
	std::string file = "ase/" + coreID + ".adf";

	if((ptr_fp = fopen(file.data(), "wb")) == NULL)
		{
			printf("Unable to open file!\n");
			exit(2);
		}else printf("Opened file successfully for writing.\n");
	
	fprintf(ptr_fp,"%d",vertices.size());
	//if( fwrite(vertices.begin(), 1,sizeof(vertices), ptr_fp) != 1)
	{
		printf("Write error!\n");
		exit(2);
	}
	fprintf(ptr_fp,"%d",uvs.size());
	if( fwrite(&uvs[0],vertices.size()*sizeof(Vector2), 1, ptr_fp) != 1)
	{
		printf("Write error!\n");
		exit(2);
	}
	fprintf(ptr_fp,"%d",normals.size());
	if( fwrite(&normals[0],vertices.size()*sizeof(Vector3), 1, ptr_fp) != 1)
	{
		printf("Write error!\n");
		exit(2);
	}
	
	fprintf(ptr_fp,"%d",texturesID.size());
	
	for ( int i = 0; i < texturesID.size(); i++){
		fprintf(ptr_fp,"%d%s",texturesID[i].size(),texturesID[i]);
		
	}
	
	printf("Write was successful.\n");
	fclose(ptr_fp);
}

void coreMesh::ReadDumpedFile(){
	std::string file = "ase/"+coreID+".adf";
	FILE * ptr_fp;

	if((ptr_fp = fopen(file.data(), "rb"))==NULL)
	{
		printf("Unable to open the file!\n");
		exit(1);
	}else printf("Opened file successfully for reading.\n");
	int n = (int)malloc(sizeof(int));
	if(fread( &n,sizeof( int ), 1, ptr_fp) != 1)
	{
		printf( "Read error!\n" );
		exit( 1 );
	}else printf( "Read was successful.\n" );
	Vector3 * x = (Vector3*) malloc ((n)*sizeof(Vector3));
	
	if( fread(x,(n)*sizeof(Vector3),1,ptr_fp) != 1)
	{
		printf( "Read error!\n" );
		exit( 1 );
	}else printf( "Read was successful.\n" );

	fclose(ptr_fp);

}